from types import MethodType


class Student(object):  # 括号里面是要继承的类，其中object是默认类。python支持多重继承
    __slots__ = ("__name", "age")  # 只允许添加sukinaHitomi属性
    
    def __init__(self, name, age):  # 构造函数
        self.__name = name
        self.age = age  # 变量前面加__表示这是一个private变量
# 由于类可以起到模板的作用，因此，可以在创建实例的时候，把一些我们认为必须绑定的属性强制填写进去。
# 通过定义一个特殊的__init__方法，在创建实例的时候，就把name，score等属性绑上去。（构造函数）
# 注意：特殊方法“__init__”前后分别有两个下划线！！！
# 注意到__init__方法的第一个参数永远是self，表示创建的实例本身。
# 因此，在__init__方法内部，就可以把各种属性绑定到self，因为self就指向创建的实例本身。
# 有了__init__方法，在创建实例的时候，就不能传入空的参数了，必须传入与__init__方法匹配的参数。
# 但self不需要传，Python解释器自己会把实例变量传进去：

    def PrintAge(self):
        print("%s的年龄是%d岁" % (self.__name, self.age))


class Joushikousei(Student):
    __slots__ = ("sukinahito", "__gumi", "PrintBookName")

    @property  # 将get函数变成属性
    def Gumi(self):
        return self.__gumi

    @Gumi.setter  # 将property对应的get函数的set函数变成属性
    def SetGumi(self, gumi):
        self.__gumi = gumi


Lemon = Student("柠檬", 24)  # 柠檬，24岁，是学生
Lemon.PrintAge()  # 用这种方式可以打出公有和私有成员
# print(Lemon.__name, lemon.age) 前半部分不可行，后半部分可行

Mashiro = Joushikousei("椎名真白", 17)
Mashiro.PrintAge()  # 可见已经成功继承了父类的公有成员函数，并体现多态

print(isinstance(Mashiro, Joushikousei))  # 判断Mashiro是否属于jk类，True
print(isinstance(Lemon, Joushikousei))  # 判断Lemon是否属于jk类，False

# 输出对象属于哪个类
print(type(Mashiro))  # 判断类型，输出<class '__main__.Joushikousei'>
# 判断对象是否属于某个类或者某个类的子类
print(isinstance(Mashiro, Student), isinstance(Mashiro, Joushikousei))
# 输出True True 因为真白属于JK类，同时JK类也是Student的子类

# 如果要获得一个对象的所有属性和方法，可以使用dir()函数，它返回一个包含字符串的list
# 比如，获得一个str对象的所有属性和方法：
print(dir(Mashiro))  # 输出Mashiro对象包含的所有属性和方法

# 以下为面向对象的其他套路
Sakura = Joushikousei("山内樱良", 17)
# 在python里面，是可以在类外给对象绑定独特的属性或者方法的，对同类的其他对象无效
# 这是绑定一个属性
Sakura.sukinahito = "Shiga Haruki"  # OK
# Sakura.osananajimi = "dare"  # 这个属性受__slot__限制不能添加
# 以下是绑定一个方法


def PrintBookName(self):
    self.age = 18
    print("Hoshi No Ouji Sama")


# 注意，绑定一个自己的方法有两种方案
# Sakura.PrintBookName = PrintBookName  # 这是第一种方案，但是只能适用于函数内不涉及self的状况
Sakura.PrintBookName = MethodType(PrintBookName, Sakura)
# Mashiro.PrintBookName()  # 这句会报错的，因为只有Sakura对象支持这个方法
Sakura.PrintBookName()
Sakura.PrintAge()  # 方法2才可以使用self

# property和setter的对比应用
Sakura.SetGumi = 2  # 通过setter，把set函数变成属性
print("Sakura的班级是", Sakura.Gumi)  # 通过property，把get函数变成属性
